//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-2003  Microsoft Corporation

Module Name: ser_intf.cpp

Abstract:


Notes:


--*/
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//

#include <Windows.h>

// serial interface
#include <pegdser.h>

#include "chanel.h"
#include "gsm0710_ioctrl.h"

#include "args.h"
#include "ioctl_cfg.h"


const LPCTSTR g_tszRegKeyRIL_MUX = TEXT("Software\\Microsoft\\RIL");

#define DATA_PORT_MUX_MODE_ITEM      TEXT("DataPortMuxMode")
#define DATA_PORT_INTERFACE_ITEM     TEXT("DataPort")


//-----------------
// Dll entry point
//-----------------

BOOL WINAPI DllMain(HANDLE hinstDll, DWORD dwReason, LPVOID lpReserved)
{
    if (DLL_PROCESS_ATTACH == dwReason)
    {
    }
    else if (DLL_PROCESS_DETACH == dwReason)
    {
    }
    return TRUE;
}


//--------------------
// COM port interface
//--------------------

DWORD COM_Init(LPCTSTR pContext, LPCVOID lpvBusContext)
{
    CUserChanel *pChanel = NULL ;
    BYTE bChanelId  = 0 ;
    HKEY hKey = NULL ;
    HKEY hRILKey = NULL ;
    BOOL bUpdateDataPortNameFlag = FALSE ;
    LONG  lStatus ;
    DWORD dwSize;
    DWORD dwDataPortSize;
    DWORD dwValType;
    DWORD dwVal;
    DWORD dwActivityTimeOut ;
    bool bATCmdChnl = true ;
    BSP_ARGS BspArgs;

#ifndef RIL_MUX_ENABLE
    return 0;
#endif

    /* Check EBOOT Configuration  */
    if (!KernelIoControl(IOCTL_GET_BSP_ARGS, NULL, 0, &BspArgs, sizeof(BspArgs), &dwSize))
    {
        SENDLOG((TEXT("COM_Init(MUX0710) Failed in KernelIoControl(IOCTL_GET_BSP_ARGS)\n")));            
        return 0;
    }

    if (!(BspArgs.FFUARTUsage&BSP_ARGS_FFUART_RIL))
    {
        SENDLOG((TEXT("COM_Init(MUX0710): FFUART had not been configured for RIL driver, RIL will not be load\n")));            
        return 0;
    }

#define MAX_SZKEY_SIZE  256
    wchar_t szName[MAX_SZKEY_SIZE];
    wchar_t szDataPortName[MAX_SZKEY_SIZE];
    wchar_t szBuiltinKey[MAX_SZKEY_SIZE] ;

    // pContext point to HKLM/drives/active/...
    // that contains the following values:
    //      - Key   -> original builtin key
    //      - Name  -> name of the loaded instance
    //-------------------------------------------
    lStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, pContext, 0, 0, &hKey) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        goto error ;
    }

    // get Key
    dwSize = MAX_SZKEY_SIZE ;
    dwValType = REG_SZ ;
    lStatus = RegQueryValueEx( hKey, TEXT("Key"), NULL, &dwValType, (LPBYTE) szBuiltinKey, &dwSize) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        goto error ;
    }

    // get Name
    dwSize = MAX_SZKEY_SIZE ;
    dwValType = REG_SZ ;
    lStatus = RegQueryValueEx( hKey, TEXT("Name"), NULL, &dwValType, (LPBYTE) szName, &dwSize) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        goto error ;
    }

    RegCloseKey(hKey) ;

    SENDLOG((TEXT("RIL: 710MUX: COM_INIT(%s) Started!\n"), szName)) ;    

    // read chanel id in registry :
    //  - value GSM_0710_DLCI under
    //-------------------------------

    lStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, szBuiltinKey, 0, 0, &hKey) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        goto error ;
    }

    // get DLCI
    dwSize = sizeof(DWORD) ;
    dwValType = REG_DWORD ;
    lStatus = RegQueryValueEx( hKey, GSM710_CHANNEL_ID, NULL, &dwValType, (LPBYTE) &dwVal, &dwSize) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        goto error ;
    }

    bChanelId = (BYTE) dwVal ;

    // get channel type
    dwSize = sizeof(DWORD) ;
    dwValType = REG_DWORD ;
    lStatus = RegQueryValueEx( hKey, GSM710_CHANNEL_TYPE, NULL, &dwValType, (LPBYTE) &dwVal, &dwSize) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        goto error ;
    }

    if (dwVal)
    {
        bATCmdChnl = true ;
    }
    else
    {
        bATCmdChnl = false ;
    }

    // get activity time-out
    dwSize = sizeof(DWORD) ;
    dwValType = REG_DWORD ;
    lStatus = RegQueryValueEx( hKey, GSM710_CHANNEL_ACTIVITY_TIMEOUT, NULL, &dwValType,
                               (LPBYTE) &dwActivityTimeOut, &dwSize) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        dwActivityTimeOut = 0 ;
    }

    // create the new chanel
    pChanel = new CUserChanel(bChanelId, bATCmdChnl, dwActivityTimeOut, szName) ;
    if ( (!pChanel) || (!pChanel->m_bInitFlag))
    {
        // chanel cannot be opened or created
        delete pChanel ;
        pChanel = NULL ;
        SENDERR((TEXT("RIL: 710MUX: COM_INIT(%s) Allocated Failed!\n"), szName)) ;
        goto error;
    }
    else
    {
        SENDLOG((TEXT("RIL: 710MUX: COM_INIT(%s) User Chanel Allocated Completed!\n"), szName)) ;
    }

    /* Update DataPort Item (MUX Mode) to register file for RIL and RIL proxy */    
    lStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE, g_tszRegKeyRIL_MUX, 0, 0, &hRILKey) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        SENDERR((TEXT("RIL: 710MUX: COM_INIT(%s) Failed in RegOpenKeyEx(%s)!\n"), szName, g_tszRegKeyRIL_MUX)) ;
        goto error ;
    }

    dwSize = MAX_SZKEY_SIZE ;
    dwValType = REG_SZ ;
    lStatus = RegQueryValueEx(hRILKey, DATA_PORT_MUX_MODE_ITEM, NULL, &dwValType, (LPBYTE) szName, &dwSize) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        SENDERR((TEXT("RIL: 710MUX: COM_INIT Failed in RegQueryValueEx(%s %s)!\n"), szName, DATA_PORT_MUX_MODE_ITEM)) ;
        goto error ;
    }

    dwDataPortSize = MAX_SZKEY_SIZE ;
    dwValType = REG_SZ ;
    lStatus = RegQueryValueEx(hRILKey, DATA_PORT_INTERFACE_ITEM, NULL, &dwValType, (LPBYTE) szDataPortName, &dwDataPortSize) ;
    if ( ERROR_SUCCESS  != lStatus )
    {
        SENDERR((TEXT("RIL: 710MUX: COM_INIT Failed in RegQueryValueEx(%s %s)!\n"), szName, DATA_PORT_INTERFACE_ITEM)) ;
        goto error ;
    }

    bUpdateDataPortNameFlag = FALSE;
    if (dwDataPortSize != dwSize)
    {
        bUpdateDataPortNameFlag = TRUE;
    }
    else
    {
        for (DWORD ulLoopI = 0; ulLoopI < dwDataPortSize/sizeof(wchar_t); ulLoopI++)
        {
            if (szDataPortName[ulLoopI] != szName[ulLoopI])
            {
                bUpdateDataPortNameFlag = TRUE;
                break;
            }
        }
    }

    if (bUpdateDataPortNameFlag)
    {
        lStatus = RegSetValueEx(hRILKey, DATA_PORT_INTERFACE_ITEM, 0, REG_SZ, (LPBYTE)szName, dwSize);
        if ( ERROR_SUCCESS  != lStatus )
        {
            SENDERR((TEXT("RIL: 710MUX: COM_INIT Failed in RegSetValueEx(%s %s)!\n"), szName, DATA_PORT_INTERFACE_ITEM)) ;
            goto error ;
        }

        lStatus = RegFlushKey(hRILKey);
        if ( ERROR_SUCCESS  != lStatus )
        {
            SENDERR((TEXT("RIL: 710MUX: COM_INIT Failed in RegFlushKey!\n"))) ;
            goto error ;
        }
    }
    
    SENDERR((TEXT("RIL: 710MUX: COM_INIT Successfully!\n"))) ;
        
error:
    if (hKey)
    {
        RegCloseKey(hKey) ;
        hKey = NULL;
    }

    if (hRILKey)
    {
        RegCloseKey(hRILKey) ;
        hRILKey = NULL;
    }

    return (DWORD) pChanel ;
}

BOOL COM_Deinit(DWORD dwData)
{
    if (dwData)
    {
        delete (CUserChanel *) dwData ;
    }

    return TRUE ;
}

DWORD COM_Open(DWORD dwData, DWORD dwAccess, DWORD dwShareMode)
{
    CUserChanel *pChanel =  (CUserChanel *) dwData ;

    if (!*pChanel)
    {
        // channel has been terminated => try to re-init
        if (!pChanel->Init())
        {
            // definitely not working
            SetLastError(ERROR_NOT_READY) ;
            return NULL ;
        }
    }

    if (DISCONNECTED == pChanel->m_lState)
    {
        // channel is disconnected => try to reconnect
        if (!pChanel->SendSABM())
        {
            SetLastError(ERROR_NOT_READY) ;
            return NULL ;
        }
    }

    // Activity monitoring
    if(pChanel->m_bMonitorActivity)
    {
        InterlockedExchange(&(pChanel->m_lLastActivityReport), CHANNEL_OPEN) ;
        pChanel->NotifyActivity(CHANNEL_OPEN) ;
    }

    return dwData ;
}

BOOL COM_Close(DWORD dwOpenData)
{
    CUserChanel *pChanel =  (CUserChanel *) dwOpenData ;

    // Activity monitoring
    if(pChanel->m_bMonitorActivity)
    {
        InterlockedExchange(&(pChanel->m_lLastActivityReport), CHANNEL_CLOSE) ;
        pChanel->NotifyActivity(CHANNEL_CLOSE) ;
    }

    // chanel is maintained opened and connnected
    // => nothing to do
    return TRUE ;
}

DWORD COM_Read(DWORD dwOpenData, LPVOID pBuf, DWORD dwLen)
{
    DWORD dwRet = 0 ;
    CUserChanel *pChanel =  (CUserChanel *) dwOpenData ;

    // rem : NULL == pBuf is tested by upper layer
    dwRet = pChanel->ReadData((BYTE*)pBuf, dwLen);

    return dwRet ;
}

DWORD COM_Write(DWORD dwOpenData, LPCVOID pBuf, DWORD dwLen)
{
    CUserChanel *pChanel =  (CUserChanel *) dwOpenData ;

    if ( !*pChanel ||
         DISCONNECTED == pChanel->m_lState )
    {
        // chanel is not usable
        SetLastError(ERROR_NOT_READY) ;
        return (DWORD)-1;
    }

    // control flow checkin => block if FCON event is not set
    WaitForSingleObject(pChanel->s_hFCONEv, INFINITE) ;

    // Activity monitoring
    if( pChanel->m_bMonitorActivity )
    {
        // update time since last activity notif
        InterlockedExchange(&(pChanel->m_lTimeSinceLastActivityNotif), GetTickCount()) ;

        // signal activity
        if ( CHANNEL_BUSY != pChanel->m_lLastActivityReport && NULL != pChanel->m_hChanelBusyEv )
        {
            SetEvent(pChanel->m_hChanelBusyEv) ;
        }
    }


    // rem : NULL == pBuf is tested by upper layer
    return pChanel->SendUIH((BYTE *) pBuf, dwLen) ;
}

BOOL COM_IOControl(DWORD dwOpenData, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
                   PDWORD pdwActualOut)
{
    CUserChanel *pChanel =  (CUserChanel *) dwOpenData ;
    BOOL fRet = FALSE;
    DWORD dwOutUsed = 0 ;

    BOOL fOk = FALSE;
    DWORD dwValue = 0;

    // TO BE DONE: handle IOCTL_PSL_NOTIFY => terminate the channel

    if ( (!*pChanel || DISCONNECTED == pChanel->m_lState) &&
        // IO controls that don't require the channel to be connected
        (IOCTL_POWER_CAPABILITIES           != dwCode &&
        IOCTL_POWER_SET                     != dwCode &&
        IOCTL_POWER_QUERY                   != dwCode &&
        IOCTL_POWER_GET                     != dwCode &&
        IOCTL_REGISTER_POWER_RELATIONSHIP   != dwCode &&
        IOCTL_MUX07_10_SWITCH_RADIO_ON      != dwCode &&
        IOCTL_MUX07_10_SWITCH_RADIO_OFF     != dwCode &&
        IOCTL_MUX07_10_EXIT_MUX_MODE        != dwCode &&
        IOCTL_MUX07_10_RESET_RADIO          != dwCode) )
    {
        SENDERR((TEXT("710MUX: Channels: -> COM_IOControl (%d) Not Ready \r\n"), dwCode)) ;        
        // chanel is not ready
        SetLastError(ERROR_NOT_READY) ;
        goto error ;
    }

    switch (dwCode)
    {
        //----------------------
        // Comm Event
        //----------------------
        case IOCTL_SERIAL_GET_WAIT_MASK :
        {
            if ( (dwLenOut < sizeof(DWORD)) || (NULL == pBufOut) ||
                (NULL == pdwActualOut) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }

            // Get the Wait Mask
            dwValue = pChanel->m_fEventMask;
            fRet = CeSafeCopyMemory( pBufOut, &dwValue, sizeof(DWORD) );
            dwOutUsed = sizeof(DWORD);

            break;
        }

        case IOCTL_SERIAL_SET_WAIT_MASK :
        {
            if ( (dwLenIn < sizeof(DWORD)) || (NULL == pBufIn) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }

            fOk = CeSafeCopyMemory( &dwValue, pBufIn, sizeof(DWORD) );
            if ( fOk )
            {
                // Set the Wait Mask
                InterlockedExchange( (LONG *) &(pChanel->m_fEventMask), dwValue ) ;

                // NOTE: If there is an outstanding WaitCommEvent, it should
                // return an error when SET_WAIT_MASK is called.
                pChanel->m_fAbort = true ;
                PulseEvent(pChanel->m_hCommEvent);
                fRet = TRUE;
            }
            break;            
        }

        case IOCTL_SERIAL_WAIT_ON_MASK :
        {
            if ( (dwLenOut < sizeof(DWORD)) || (NULL == pBufOut) ||
                 (NULL == pdwActualOut) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }

            // reset answer
            //---------------
            dwOutUsed = sizeof(DWORD);

            if ( !pChanel->m_fEventMask )
            {
                // wait event mask is cleared
                break ;
            }
            else if (DISCONNECTED == pChanel->m_lState )
            {
                // channel is disconnected
                SetLastError(ERROR_NOT_READY) ;
                break;
            }

            // reset EV_RXCHAR if no byte to read
            //-------------------------------------

            if (0 == pChanel->GetRxBytesAvailable())
            {
                pChanel->m_fEventData &= ~EV_RXCHAR ;
            }

            // check events
            //--------------

            // Abort should only affect us once we have started waiting => Ignore any old aborts
            pChanel->m_fAbort = false ;

            if ( !(pChanel->m_fEventData & pChanel->m_fEventMask) )
            {
                // no event already signaled => wait for one
                WaitForSingleObject( pChanel->m_hCommEvent, INFINITE) ;
                if(pChanel->m_bTerminate)
                {
                    // connection have been terminated
                    // return FALSE
                    SetLastError(ERROR_NOT_READY) ;
                    break ;
                }
            }

            if ( CONNECTED == pChanel->m_lState )
            {
                if ( !pChanel->m_fAbort  )
                {
                    dwValue = InterlockedExchange( (LONG *) &(pChanel->m_fEventData), 0 ) ;
                }

                fRet = TRUE ;
            }
            else
            {
                SetLastError(ERROR_NOT_READY) ;
            }

            if ( fRet )
            {
                fRet = CeSafeCopyMemory( pBufOut, &dwValue, sizeof(DWORD) );
            }
            
            break;
        }

        //----------------------
        // Time out
        //----------------------
        case IOCTL_SERIAL_SET_TIMEOUTS :
        {
            if ( (dwLenIn < sizeof(COMMTIMEOUTS)) || (NULL == pBufIn) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }

            // save Time-out structure
            fRet = CeSafeCopyMemory( &pChanel->m_CommTimeouts, pBufIn, sizeof(COMMTIMEOUTS) );

            // reset read
            dwOutUsed = 0 ;
            break ;
        }


        case IOCTL_SERIAL_GET_TIMEOUTS :
        {
            if ( (dwLenOut < sizeof(COMMTIMEOUTS)) || (NULL == pBufOut) ||
                (NULL == pdwActualOut) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }

            // copy time-out structure            
            fRet = CeSafeCopyMemory( pBufOut, &(pChanel->m_CommTimeouts), sizeof(COMMTIMEOUTS) );
            dwOutUsed = sizeof(COMMTIMEOUTS);
            break;
        }


        //----------------------
        // Status
        //----------------------
        case IOCTL_SERIAL_GET_COMMSTATUS :
        {
            if ( (dwLenOut < sizeof(SERIAL_DEV_STATUS)) || (NULL == pBufOut) ||
                 (NULL == pdwActualOut) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }
            
            // Clear the structure
            SERIAL_DEV_STATUS sds;
            memset(&sds, 0, sizeof(SERIAL_DEV_STATUS));

            // ??? fCtsHold, fDsrHold
            // set:
            //  - cbInQue
            //
            // others (cbOutQue, fXoffHold, fXoffSent, fTXim, fRLSDHold) are 0
            sds.ComStat.cbInQue = pChanel->GetRxBytesAvailable();

            fRet = CeSafeCopyMemory( pBufOut, &sds, sizeof(SERIAL_DEV_STATUS) );
            dwOutUsed = sizeof(SERIAL_DEV_STATUS);            
            break ;
        }


        case IOCTL_SERIAL_GET_MODEMSTATUS :
        {
            if ( (dwLenOut < sizeof(DWORD)) || (NULL == pBufOut) ||
                 (NULL == pdwActualOut) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }

            // get modem status
            dwValue = pChanel->GetModemStatus();            
            fRet = CeSafeCopyMemory( pBufOut, &dwValue, sizeof(DWORD) );
            dwOutUsed = sizeof(DWORD);            
            break ;
        }


        //----------------------------
        // properties and parameters
        //----------------------------
        case IOCTL_SERIAL_GET_PROPERTIES :
        {
            if ( (dwLenOut < sizeof(COMMPROP)) || (NULL == pBufOut) ||
                 (NULL == pdwActualOut) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }        
            // Clear the ComMProp structure
            COMMPROP cp;
            memset(&cp, 0, sizeof(COMMPROP));

            // fill the comm properties structure
            cp.wPacketLength        = 0xffff ;
            cp.wPacketVersion       = 0xffff ;
            cp.dwServiceMask        = SP_SERIALCOMM ;
            cp.dwMaxTxQueue         = pChanel->MaxFrameData();
            cp.dwMaxRxQueue         = pChanel->RecBufSize() ;
            cp.dwMaxBaud            = BAUD_115200 ;
            cp.dwProvSubType        = PST_RS232 ;

            // XON/XOFF is not supported (=> no X Char as well)
            // parity is by default set to none (see point below)
            cp.dwProvCapabilities   = PCF_DTRDSR | PCF_RLSD | PCF_RTSCTS |
                                      PCF_INTTIMEOUTS | PCF_TOTALTIMEOUTS ;

            //  capabilities are limited since Remote Port Negotiation Command
            //  (RPM)is not supported
            //---------------------------------------------------------------
            cp.dwSettableParams     = 0 ;
            cp.dwSettableBaud       = BAUD_115200 ;
            cp.wSettableData        = DATABITS_8 ;
            cp.wSettableStopParity  = STOPBITS_10 | PARITY_NONE ;

            fRet = CeSafeCopyMemory( pBufOut, &cp, sizeof(COMMPROP) );
            dwOutUsed = sizeof(COMMPROP);
            break ;
        }


        case IOCTL_SERIAL_GET_DCB :
        {
            if ( (dwLenOut < sizeof(DCB)) || (NULL == pBufOut) ||
                 (NULL == pdwActualOut) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }

            // copy DCB structure
            //--------------------
            fRet = CeSafeCopyMemory( pBufOut, &pChanel->m_dcb, sizeof(DCB) );
            dwOutUsed = sizeof(DCB);
            break ;
        }


        case IOCTL_SERIAL_SET_DCB :
        {
            if ( (dwLenIn < sizeof(DCB)) || (NULL == pBufIn) )
            {
                SetLastError (ERROR_INVALID_PARAMETER);
                break;
            }

            // save DCB structure
            //
            // Rem :
            //      - nothing else to do since this is a virtual COM port
            //    and since Remote Port Negotiation Command
            //        (RPM)is not supported
            //------------------------------------------------------------
            fRet = CeSafeCopyMemory( &pChanel->m_dcb, pBufIn, sizeof(DCB) );
            dwOutUsed = 0 ;
            break ;
        }


        //----------------------
        // Signals
        //----------------------
        case IOCTL_SERIAL_SET_RTS :
        case IOCTL_SERIAL_CLR_RTS :
        case IOCTL_SERIAL_SET_DTR :
        case IOCTL_SERIAL_CLR_DTR :
        case IOCTL_SERIAL_SET_XOFF :
        case IOCTL_SERIAL_SET_XON :
        {
            fRet = pChanel->SendModemStatus(dwCode);
            break ;
        }


        //----------------------
        // Power management
        //----------------------
        case IOCTL_POWER_CAPABILITIES :
        {
            if (NULL == pBufOut || dwLenOut < sizeof(POWER_CAPABILITIES) || NULL == pdwActualOut)
            {
                SetLastError(ERROR_INVALID_PARAMETER);
                break;
            }      
            // Clear the structure
            POWER_CAPABILITIES pc;
            memset(&pc, 0, sizeof(POWER_CAPABILITIES));

            // support :
            //  - D0 (full on)
            //  - D2 (standby)
            //  - D3 (sleep)
            //  - D4 (off)
            pc.DeviceDx = 0x10 | 0x08 | 0x04 | 0x01 ;

            fRet = CeSafeCopyMemory( pBufOut, &pc, sizeof(POWER_CAPABILITIES) );
            dwOutUsed = sizeof(POWER_CAPABILITIES);
            break ;
        }


        case IOCTL_POWER_SET :
        {
            if (NULL == pBufOut || dwLenOut < sizeof(CEDEVICE_POWER_STATE) || NULL == pdwActualOut)
            {
                SetLastError(ERROR_INVALID_PARAMETER);
                break;
            }
        
            CEDEVICE_POWER_STATE cps = PwrDeviceUnspecified;
            fOk = CeSafeCopyMemory( &cps, pBufOut, sizeof(CEDEVICE_POWER_STATE) );
            if ( fOk )
            {
                switch( cps )
                {
                    case D0 :
                        pChanel->m_eLastPowerState = D0 ;
                        break ;

                    case D1 :
                    case D2 :
                        pChanel->m_eLastPowerState = D2 ;
                        break ;

                    case D3 :
                        // indicate sleep mode to TE
                        //pChanel->s_Dispatcher.EnterPowerSavingMode() ;
                        pChanel->m_eLastPowerState = D3 ;
                        break ;

                    case D4 :
                        pChanel->m_eLastPowerState = D4 ;
                        break ;

                    default :
                        // unknown state
                        SetLastError(ERROR_INVALID_PARAMETER);
                        goto error ;
                }

                fRet = CeSafeCopyMemory( pBufOut, &pChanel->m_eLastPowerState, sizeof(CEDEVICE_POWER_STATE) );
                dwOutUsed = sizeof(CEDEVICE_POWER_STATE);
            }

            break ;
        }
        case IOCTL_POWER_QUERY :
        case IOCTL_POWER_GET :
        {
            SENDLOG((TEXT("710MUX: Channels: -> IOCTL_POWER_GET (QUERY) \r\n"))) ;                
            if (NULL == pBufOut || dwLenOut < sizeof(CEDEVICE_POWER_STATE) || NULL == pdwActualOut)
            {
                SetLastError(ERROR_INVALID_PARAMETER);
                break;
            }

            fRet = CeSafeCopyMemory( pBufOut, &pChanel->m_eLastPowerState, sizeof(CEDEVICE_POWER_STATE) );
            dwOutUsed = sizeof(CEDEVICE_POWER_STATE);
            break ;
        }

        case IOCTL_REGISTER_POWER_RELATIONSHIP :
        {
            SetLastError (ERROR_NOT_SUPPORTED) ;
            break ;
        }

        //--------------------------------------
        // Switching on or off the radio module
        //--------------------------------------
        case IOCTL_MUX07_10_SWITCH_RADIO_OFF :
        {
            SENDERR((TEXT("710MUX: Channels: -> IOCTL_MUX07_10_SWITCH_RADIO_OFF \r\n"))) ;            

            if (!(*pChanel))
            {
                // radio is already off
                SENDERR((TEXT("710MUX: Channels: -> radio is already off\r\n"))) ;
                fRet = TRUE ;
            }
            else
            {
                // terminate the MUX
                //
                // Rem : must terminate from the MUX since
                //               terminating from the channel will
                //               only terminate the concerned channel
                //               and not the whole MUX
                pChanel->s_Dispatcher.Terminate() ;

                // switch the radio off
                pChanel->s_Dispatcher.SwitchRadioOff() ;

                fRet = TRUE ;
            }

            break ;
        }        

        case IOCTL_MUX07_10_SWITCH_RADIO_ON :
        {
            SENDERR((TEXT("710MUX: Channels: -> IOCTL_MUX07_10_SWITCH_RADIO_ON \r\n"))) ;
            
            if (!(*pChanel))
            {
                // Initialize Mux and all registered channels
                //
                // Rem : radio will be switch on during Mux init
                if (pChanel->s_Dispatcher.ReinitAll())
                {
                    fRet = TRUE ;
                }
                else
                {
                    SENDERR((TEXT("710MUX: Channels: -> ERROR cannot reinit channels\r\n"))) ;
                }
            }
            else
            {
                // radio is already on
                fRet = TRUE ;
            }

            break ;
        }


        case IOCTL_MUX07_10_RESET_RADIO :
        {
            SENDLOG((TEXT("710MUX: Channels: -> IOCTL_MUX07_10_RESET_RADIO \r\n"))) ;
            
            // terminate dispatcher
            pChanel->s_Dispatcher.Terminate() ;

            // indicate that next init will required radio to be reseted
            pChanel->s_Dispatcher.m_bResetRequired = true ;

            // Initialize Mux and all registered channels
            //
            // Rem : radio will be switch on during Mux init
            if (pChanel->s_Dispatcher.ReinitAll() && NULL != pChanel->s_Dispatcher.m_hRebootOKEv)
            {
                // signal reboot
                fRet = TRUE ;
                SetEvent(pChanel->s_Dispatcher.m_hRebootOKEv) ;
            }
            else
            {
                SENDERR((TEXT("710MUX: Channels: -> ERROR cannot reset radio\r\n"))) ;
            }

            // reset is done (either if it has failed or not)
            pChanel->s_Dispatcher.m_bResetRequired = false ;
            break ;
        }
        /* Exit Mux Mode */
        case IOCTL_MUX07_10_EXIT_MUX_MODE:
        {
            SENDLOG((TEXT("710MUX: Channels: -> IOCTL_MUX07_10_EXIT_MUX_MODE \r\n"))) ;            

            if (!(*pChanel))
            {
                // radio is already off
                SENDERR((TEXT("710MUX: Channels: -> radio is already off\r\n"))) ;
                fRet = TRUE ;
            }
            else
            {
                // terminate the MUX
                //
                // Rem : must terminate from the MUX since
                //               terminating from the channel will
                //               only terminate the concerned channel
                //               and not the whole MUX
                pChanel->s_Dispatcher.Terminate() ;
                pChanel->s_Dispatcher.TerminateSerialIntf() ;
                fRet = TRUE ;
            }

            break ;
        }        


        // Unsupported
        //----------------------
        case IOCTL_SERIAL_PURGE :
        case IOCTL_SERIAL_SET_QUEUE_SIZE :
        case IOCTL_SERIAL_IMMEDIATE_CHAR :
        case IOCTL_SERIAL_SET_BREAK_ON :
        case IOCTL_SERIAL_SET_BREAK_OFF :
        case IOCTL_SERIAL_ENABLE_IR :
        case IOCTL_SERIAL_DISABLE_IR :
        {
            SetLastError (ERROR_NOT_SUPPORTED) ;
        }
        break ;

        //----------------------
        // Invalid IO ctrl code
        //----------------------
        default :
        {
            SetLastError (ERROR_INVALID_PARAMETER);
        }
        break ;
    }

error:
    if (fRet && pdwActualOut)
    {
        fRet = CeSafeCopyMemory( pdwActualOut, &dwOutUsed, sizeof(DWORD) );
    }

    return fRet;
}

// unsupported feature
//---------------------

//
// COM seek
//
DWORD COM_Seek(DWORD dwOpenData, long pos, DWORD type)
{
    return (DWORD)-1;
}


//
// COM power-up
//
void COM_PowerDown(DWORD dwData)
{
    CUserChanel *pChanel =  (CUserChanel *) dwData ;

    // change state to off
    pChanel->m_eLastPowerState = D4 ;
}


//
// COM power-down
//
void COM_PowerUp(DWORD dwData)
{
    CUserChanel *pChanel =  (CUserChanel *) dwData ;

    // change state to sleep
    pChanel->m_eLastPowerState = D3 ;
}


